﻿using System;
using System.Collections.Generic;
using System.Linq;
using CncConvProg.Geometry.Entity;
using CncConvProg.Model.ConversationalStructure.Abstraction;
using CncConvProg.Model.ConversationalStructure.Lavorazioni.Common;
using CncConvProg.Model.PathGenerator;
using CncConvProg.Model.Tool;
using CncConvProg.Model.Tool.Mill;
using CncConvProg.Geometry;
using CncConvProg.Model.Tool.Parametro;

namespace CncConvProg.Model.ConversationalStructure.Lavorazioni.Fresatura
{
    [Serializable]
    public sealed class Spianatura : LavorazioneFresatura
    {
        public Spianatura()
            : base()
        {
            LivelloZ = 0;

            PuntoStartX = 0;
            PuntoStartY = 0;

            StartPoint = SquareShapeHelper.SquareShapeStartPoint.Center;

            Sgrossatura = new Operazione(this, LavorazioniEnumOperazioni.FresaturaSpianaturaSgrossatura);

            Finitura = new Operazione(this, LavorazioniEnumOperazioni.FresaturaSpianaturaFinitura);
        }

        public override List<Operazione> Operazioni
        {
            get
            {
                return new List<Operazione> { Sgrossatura, Finitura };
            }
        }

        public SquareShapeHelper.SquareShapeStartPoint StartPoint { get; set; }

        public Operazione Sgrossatura { get; set; }

        public Operazione Finitura { get; set; }

        public double SovrametalloPerFinitura { get; set; }

        public double Sovrametallo { get; set; }

        public double LivelloZ { get; set; }

        public double Larghezza { get; set; }

        public double Altezza { get; set; }

        public double PuntoStartX { get; set; }

        public double PuntoStartY { get; set; }

        //internal override Utensile CreateTool(LavorazioniEnumOperazioni enumOperationType)
        //{
        //    var unit = Singleton.Instance.MeasureUnit;

        //    //switch ((SpianaturaEnumOperazioni)(enumOperationType))
        //    //{

        //    //    case SpianaturaEnumOperazioni.Sgrossatura:
        //    //    case SpianaturaEnumOperazioni.Finitura:
        //            return new FresaSpianare(unit);

        //    //}

        //    throw new NotImplementedException();
        //}

        //internal override List<Utensile> GetCompatibleTools(LavorazioniEnumOperazioni operationType, MagazzinoUtensile magazzino)
        //{
        //    var unit = Singleton.Instance.MeasureUnit;

        //    var tools = magazzino.GetTools<FresaSpianare>(unit);

        //    return new List<Utensile>(tools);
        //}


        public override string Descrizione
        {
            get { return MecPrev.Resources.GuiRes.FaceMilling + " " + Larghezza + " x " + Altezza; }
        }

        //private Point2D GetCenterPoint()
        //{
        //    switch (StartPoint)
        //    {
        //        case SquareShapeStartPoint.UpLeft:
        //            return new Point2D { X = PuntoStartX + Larghezza / 2, Y = PuntoStartY - Altezza / 2 };

        //        case SquareShapeStartPoint.UpRight:
        //            return new Point2D { X = PuntoStartX - Larghezza / 2, Y = PuntoStartY - Altezza / 2 };

        //        case SquareShapeStartPoint.DownLeft:
        //            return new Point2D { X = PuntoStartX + Larghezza / 2, Y = PuntoStartY + Altezza / 2 };

        //        case SquareShapeStartPoint.DownRight:
        //            return new Point2D { X = PuntoStartX - Larghezza / 2, Y = PuntoStartY + Altezza / 2 };

        //        case SquareShapeStartPoint.Center:
        //        default:
        //            return new Point2D(PuntoStartX, PuntoStartY);
        //    }
        //}

        protected override List<IEntity3D> GetFinalPreview()
        {
            var centerPoint = GetCenterPoint();

            var l1 = new Line3D
                         {
                             Start = new Point3D
                                         {
                                             X = centerPoint.X + Larghezza / 2,
                                             Y = centerPoint.Y + Altezza / 2,
                                             Z = LivelloZ,
                                         },

                             End = new Point3D
                                       {
                                           X = centerPoint.X + Larghezza / 2,
                                           Y = centerPoint.Y - Altezza / 2,
                                           Z = LivelloZ,
                                       }
                         };

            var l2 = new Line3D
                         {
                             Start = l1.End,

                             End = new Point3D
                                       {
                                           X = centerPoint.X - Larghezza / 2,
                                           Y = centerPoint.Y - Altezza / 2,
                                           Z = LivelloZ,
                                       }
                         };

            var l3 = new Line3D
                         {
                             Start = l2.End,

                             End = new Point3D
                                       {
                                           X = centerPoint.X - Larghezza / 2,
                                           Y = centerPoint.Y + Altezza / 2,
                                           Z = LivelloZ,
                                       }
                         };

            var l4 = new Line3D
                         {
                             Start = l3.End,
                             End = l1.Start,
                         };

            var rslt = new List<IEntity3D> { l1, l2, l3, l4 };

            foreach (var entity2D in rslt)
            {
                entity2D.PlotStyle = EnumPlotStyle.Element;
            }

            return rslt;
        }

        private Point2D GetCenterPoint()
        {
            return SquareShapeHelper.GetCenterPoint(StartPoint, PuntoStartX, PuntoStartY, Larghezza, Altezza);
        }

        protected override void CreateSpecificProgram(ProgramPhase programPhase, Operazione operazione)
        {
            var parametro = operazione.GetParametro<ParametroFresaSpianare>();

            if (parametro == null)
                throw new NullReferenceException();

            var larghezzaPassata = parametro.GetLarghezzaPassata();

            var profPassata = parametro.GetProfonditaPassata();

            var diaFresa = parametro.DiametroMinimoFresa;

            diaFresa -= parametro.GetRaggioInserto() * 2;

            var diaIngombroFresa = parametro.DiametroIngombroFresa;

            var sovraMetallo = Sovrametallo;

            var moveCollection = new MoveActionCollection();

            var centerPoint = GetCenterPoint();

            var secZ = LivelloZ + ExtraCorsa + sovraMetallo;

            switch ((LavorazioniEnumOperazioni)operazione.OperationType)
            {
                case LavorazioniEnumOperazioni.FresaturaSpianaturaSgrossatura:
                    {
                        var zLavoro = LivelloZ + SovrametalloPerFinitura;

                        OneDirectionFaceMilling(moveCollection, centerPoint.X, centerPoint.Y, Larghezza, Altezza, larghezzaPassata, diaFresa, diaIngombroFresa, sovraMetallo, profPassata, zLavoro, secZ, ExtraCorsa);


                    } break;

                case LavorazioniEnumOperazioni.FresaturaSpianaturaFinitura:
                    {

                        OneDirectionFaceMilling(moveCollection,
                            centerPoint.X, centerPoint.Y,
                            Larghezza, Altezza, larghezzaPassata,
                            diaFresa,
                            diaIngombroFresa,
                            SovrametalloPerFinitura,
                            profPassata,
                            LivelloZ,
                            secZ,
                            ExtraCorsa
                            );
                    } break;

            }

            var mm = base.GetFinalProgram(moveCollection);

            foreach (var variable in mm)
            {
                programPhase.AddMoveAction(variable);
            }
        }

        private static void OneDirectionFaceMilling(MoveActionCollection programPhase,
                                            double offsetX, double offsetY,
                                            double larghezza, double altezza,
                                            double larghPassata, double diaFresa, double diaIngombroFresa,
                                            double sovraMetallo, double profPassata,
                                            double zFinale, double zSicurezza,
                                            double extraCorsaSicurezza)
        {
            if (CheckValueHelper.GreatherOrEqualZero(new[] { extraCorsaSicurezza }) ||
                CheckValueHelper.GreatherThanZero(new[] { larghezza, altezza, larghPassata, diaFresa, diaIngombroFresa, profPassata, }) ||
                CheckValueHelper.GreatherThan(new[]
                                              {
                                                  new KeyValuePair<double, double>(zSicurezza, zFinale + sovraMetallo),
                                                  new KeyValuePair<double, double>(diaFresa, larghPassata),
                                              })
              ) return;



            if (sovraMetallo == 0)
                sovraMetallo = profPassata;

            var zCurrent = zFinale + sovraMetallo;

            var xIni = offsetX - larghezza / 2;
            var xFin = offsetX + larghezza / 2;

            var yIni = offsetY + altezza / 2 + diaFresa / 2;
            var yFin = offsetY - altezza / 2;

            xIni -= (diaIngombroFresa / 2) + extraCorsaSicurezza;
            xFin += (diaIngombroFresa / 2) + extraCorsaSicurezza;

            // Itero per tutte le passate in Z
            while (zCurrent > zFinale)
            {

                zCurrent -= profPassata;

                if (zCurrent < zFinale)
                    zCurrent = zFinale;
                {
                    OneDirectionHorizontal(programPhase, zCurrent, zSicurezza, xIni, xFin, yIni, yFin, larghPassata);
                }

            }
            /*
             * Pensare a come riutilizzarla per direzione orizzontale e verticale
             */
        }

        private static void OneDirectionHorizontal(MoveActionCollection programPhase, double currentZ, double returnZ, double xIni, double xFin, double yIni, double yFin, double larghezzaPassata)
        {
            var currentY = yIni;

            while (currentY > yFin)
            {
                currentY -= larghezzaPassata;

                if (currentY < yFin)
                    currentY = yFin;


                // Mi sposto sopra punto attaco 
                programPhase.AddLinearMove(MoveType.Rapid, AxisAbilited.Xy, xIni, currentY, null);

                // Vado a Z sicurezza
                programPhase.AddLinearMove(MoveType.Rapid, AxisAbilited.Z, null, null, returnZ);


                // Vado a z di lavoro
                programPhase.AddLinearMove(MoveType.SecureRapidFeed, AxisAbilited.Z, null, null, currentZ);

                // Movimento di lavoro
                programPhase.AddLinearMove(MoveType.Work, AxisAbilited.Xy, xFin, null, null);

                // Stacco in rapido
                programPhase.AddLinearMove(MoveType.Rapid, AxisAbilited.Z, null, null, returnZ);

            }

        }

    }
}